/*
    用来捕捉构建过程中的编译命令，并将捕捉到的命令发送给python脚本处理
    1. 设置包装函数
    2. 将拦截到的发给python脚本处理
    3. 自己再调用原函数, 保持原先的环境变量，如果拦截到不感兴趣的执行命令，就执行原命令
    gcc case:
        1. gcc 可能是编译，编译加链接, 链接三种命令

    环境变量：
    1. 环境变量中载入PRELOAD的库路径， IRGEN的根路径，以及各种工具可能的命名集合

    TODO LIST:
    1. 环境变量载入与释放    ok
    2. execv 一族命令的捕捉
    3. posix_spwan
*/

#define _GNU_SOURCE

#include "config.h"

#include <stddef.h>
#include <libgen.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <sys/stat.h>

#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
// posix_unix环境下使用
#include <spawn.h>
#endif

// 用来配置需要跟踪的库函数

// 环境变量
#define LD_PRELOAD "LD_PRELOAD"
#define IRGEN_PRELOAD "IRGEN_PRELOAD"
#define IRGEN_PATH "IRGEN_PATH"
#define GCC_SET "IRGEN_GCC_SET"
#define CLANG_SET "IRGEN_CLANG_SET"
#define LINK_SET "IRGEN_LINK_SET"
#define ARCHIVE_SET "IRGEN_ARCHIVE_SET"
#define OBJCOPY_SET "IRGEN_OBJCOPY_SET"
#define RANLIB_SET "IRGEN_RANLIB_SET"
#define STRIP_SET "IRGEN_STRIP_SET"
#define CCACHE_SET "IRGEN_CCACHE_SET"
#define IRGEN_FILECHANGE_SCRIPT "IRGEN_FILECHANGE_SCRIPT"

#define FIELDESCRIPTORDIR "/proc/self/fd"

#define ERROR_PRINT(MESSAGE) \
    perror(MESSAGE);        \
    exit(EXIT_FAILURE);


#define CAP_LOG(command) \
    char *const *it = command;  \
    printf("[Captured Cmd]:");  \
    while(it && *it){           \
        printf(" %s", *it);     \
        ++ it;                  \
    }                           \
    printf("\n");


#define EXE_LOG(command) \
    char *const *it = command;  \
    printf("[Fake Exec]:");     \
    while(it && *it){           \
        printf(" %s", *it);     \
        ++it;                   \
    }                           \
    printf("\n");

#define LOG_DISABLE  (0)
#define LOG_FATAL    (1)
#define LOG_ERR      (2)
#define LOG_WARN     (3)
#define LOG_INFO     (4)
#define LOG_DBG      (5)

static FILE *debug_file;
static char *debug_file_path = "/tmp/IRGEN_CAPTURE_DEBUG.log";
static int debug_level = LOG_INFO;

#define LOG(level, string) do{\
		if( level <= debug_level ) {\
		    debug_file = fopen(debug_file_path, "a");\
		    if (debug_file) {\
                fprintf(debug_file, "%s:%d:\t", __FILE__, __LINE__);\
                fprintf(debug_file, "%s", string);\
                fprintf(debug_file, "\n");\
                fflush(debug_file);\
                fclose(debug_file);\
			}\
		}\
} while(0)


// 用于索引捕捉环境变量
enum{
    // IRGEN_LDPRELOAD_I = 0,
    IRGEN_PRELOAD_ENV_I = 0,
    IRGEN_PATH_ENV_I,
    IRGEN_GCC_ENV_I,
    IRGEN_CLANG_ENV_I,
    IRGEN_LINK_ENV_I,
    IRGEN_ARCHIVE_ENV_I,
    IRGEN_OBJCOPY_ENV_I,
    IRGEN_RANLIB_ENV_I,
    IRGEN_STRIP_ENV_I,
    IRGEN_CCACHE_ENV_I,
    IRGEN_FILECHANGE_SCRIPT_I,
    ENV_SIZE
};

// 系统环境变量
extern char **__environ;

typedef char const *env_t[ENV_SIZE];

static int capture_env_init = 0;

// 捕捉环境变量key
static env_t capture_env_names = {
    // LD_PRELOAD,
    IRGEN_PRELOAD,
    IRGEN_PATH,
    GCC_SET,
    CLANG_SET,
    LINK_SET,
    ARCHIVE_SET,
    OBJCOPY_SET,
    RANLIB_SET,
    STRIP_SET,
    CCACHE_SET,
    IRGEN_FILECHANGE_SCRIPT
};

//捕捉环境变量value
static env_t capture_env = {
    // 0, // LD_PRELOAD
    0, // IRGEN_PRELOAD
    0, // IRGEN_PATH
    0, // GCC_SET
    0, // CLANG_SET
    0, // LINK_SET
    0, // ARCHIVE_SET
    0, // OBJCOPY_SET
    0, // RANLIB_SET
    0, // STRIP_SET
    0, // CCACHE_SET
    0  // IRGEN_FILECHANGE_SCRIPT
};

// 清除环境变量列表，用于发于python脚本处理时需要卸载掉部分环境变量
const static char *unset_env_names[] = {
    "LD_PRELOAD"
    };

const static int unset_env_length = sizeof(unset_env_names) / sizeof(unset_env_names[0]); 

// 过滤的binary列表
char *IGNORE_LIST[] = { "cat", "rm", "mv", "/bin/bash", "sed", "/bin/sed", "as", 0 };

char *SAVE_SOURCE_LIST[] = { ".c",  ".h", 0};

static void load_env(void) __attribute__((constructor)); 
static void unload_env(void) __attribute__((destructor));
static int load_capture_env(env_t *env);
static void release_capture_env(env_t *env);
static char *python_handler_assign(char const *compiler);
static int str_in_strs(const char *str, const char **strs, size_t num);
static int size_of_array(char * const *array);
static char ** string_array_copy(char * const in[]);
static char ** update_exec_env(char * const envp[], env_t *cap_env);
static void free_fake_argv(char * const* argv);
static void update_exec_enviorn(char *envp[], char const *env_key, char const *env_value);
static int extract_comps_and_wrapper(const char *comp_wrapper_string, char ***ptr_comps, char **ptr_path_to_wrapper);
static char ** generate_fake_argv(char *python_script, const char * path, char * const*argv);
static void free_ptr_array(char *const*);
static char *const* convert_to_ptrs_array(const char *item, va_list *items);
static void print_argv(char *const *argv);
static void unset_exec_env(char **env, const char **unset_env_names, const int unset_env_length);
static char *array_to_string(char *const *array);
static int is_relative_path(char const *);
static char *get_filepath_by_fd(int);
static char *get_relative_base_path(int);
static int filter_useless_file(char const*);
static int is_contains(char const*, char const *);

// 调用库函数的包装
#ifdef HAVE_EXECVE
int execve_wrapper(const char *path, char *const argv[], char *const envp[], int unset);
int execve(const char *path, char *const argv[], char *const envp[]);
#endif

#ifdef HAVE_EXECL
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
int execl_wrapper(char const *path, char *const *argv, char *const *envp, int unset);
#endif

#ifdef HAVE_EXECV
int execv(const char *pathname, char *const argv[]);
int execv_wrapper(const char *pathname, char *const argv[], char *const *envp, int unset);
#endif

#ifdef HAVE_EXECVPE
int execvpe(const char *file, char *const argv[], char *const envp[]);
int execvpe_wrapper(const char* path, char *const *argv, char *const envp[], int unset);
#endif

#ifdef HAVE_EXECVP
int execvp(const char *file, char *const argv[]);
int execvp_wrapper(const char *file, char *const argv[], char *const *envp, int unset);
#endif

#ifdef HAVE_EXECLE
int execle(const char *pathname, const char *arg, ... /*, (char *) NULL, char *const envp[] */);
int execle_wrapper(const char *file, char * const *argv, char *const *envp, int unset);
#endif


#ifdef HAVE_EXECLP
int execlp(const char *file, const char *arg, ...);
int execlp_wrapper(const char *file, char *const *argv, char *const *envp, int unset);
#endif

#ifdef HAVE_POSIX_SPAWN
int posix_spawn(pid_t *restrict pid, const char *restrict path,
		const posix_spawn_file_actions_t *file_actions,
		const posix_spawnattr_t *restrict attrp,
		char *const argv[restrict], char *const envp[restrict]);
int posix_spawn_wrapper(pid_t *restrict pid, const char *restrict path,
		const posix_spawn_file_actions_t *file_actions,
		const posix_spawnattr_t *restrict attrp,
		char *const argv[restrict], char *const envp[restrict], int unset);
#endif

#ifdef HAVE_POSIX_SPAWNP
int posix_spawnp(pid_t *restrict pid, const char *restrict path,
		const posix_spawn_file_actions_t *file_actions,
		const posix_spawnattr_t *restrict attrp,
		char *const argv[restrict], char *const envp[restrict]);
int posix_spawnp_wrapper(pid_t *restrict pid, const char *restrict path,
		const posix_spawn_file_actions_t *file_actions,
		const posix_spawnattr_t *restrict attrp,
		char *const argv[restrict], char *const envp[restrict], int unset);
#endif

#ifdef HAVE_REMOVE
int remove(const char *path);
#endif

#ifdef HAVE_UNLINK
int unlink(const char *path);
#endif

#ifdef HAVE_UNLINKAT
int unlinkat(int fd, const char *path, int flag);
#endif

#ifdef HAVE_RENAME
int rename(const char *old, const char *new);
#endif

#ifdef HAVE_RENAMEAT
int renameat(int oldfd, const char *old, int newfd, const char *new);
#endif

#define GENERATE_HANDLE_COMMAND(file)\
    size_t len = strlen("python") + strlen(capture_env[IRGEN_FILECHANGE_SCRIPT_I]) + strlen(file) + 3;\
    char *cmd = (char *)calloc(len, sizeof(char));\
    snprintf(cmd, len, "%s %s %s", "python", capture_env[IRGEN_FILECHANGE_SCRIPT_I], file);

#define EXECUTE_CLEAN_COMMAND(command) do{\
        char *raw_env = getenv(LD_PRELOAD);\
        unsetenv(LD_PRELOAD);\
        system(command);\
        setenv(LD_PRELOAD, raw_env, 0);\
        free(command);\
    }while(0);


// 在编译器列表中寻找对应编译器是否存在于列表中
#define SEARCH_COMPS(compiler, compilers, str, comps_size) \
    str_in_strs(compiler, compilers, comps_size) ? strdup(str) : "IGNORE";

// 获取原有库函数指针
#define DLSYM(TYPE_, VAR_, SYMBOL_)                                            \
    union {                                                                    \
        void *from;                                                            \
        TYPE_ to;                                                              \
    } cast;                                                                    \
    if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) {                        \
        ERROR_PRINT("DLSYM Failed");                                            \
    }                                                                          \
    TYPE_ const VAR_ = cast.to;

// 确定返回的字符串
#define RETURN_STR(str, str1, strs)     \
    free((void *)strs);                 \
    if(strcmp(str, "IGNORE") != 0){     \
        return str;                     \
    }                                   

// extern char **environ;
//        int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
//        int execle(const char *path, const char *arg0, ... /*,
//            (char *)0, char *const envp[]*/);
//        int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
//        int execv(const char *path, char *const argv[]);
//        int execve(const char *path, char *const argv[], char *const envp[]);
//        int execvp(const char *file, char *const argv[]);
//        int execvpe(const char *file, char *const argv[], char *const envp[]);

//        int fexecve(int fd, char *const argv[], char *const envp[]);
// fexecve暂时不处理

// 函数名带l的表示参数列表是由可变参数提供的， 可变参数列表由NULL结尾
// 函数名带p的表示会在PATH中寻找执行文件
// 函数名到e的表示将传递环境变量给新进程

// 将可变参数转换成参数数组来处理
// 不带e的就用当前环境变量传递给新进程

// 首先我们需要被调用的工具是什么，分发给python处理脚本

// free_fake_argv(new_argv);                                \
//         free(python_script);                                \

#define EXEC_MACRO(func, path, argv, envp)                \
    char *python_script = python_handler_assign(basename(argv[0]));   \
    int status = 0;                                             \
    if(strcmp(python_script, "IGNORE") != 0){               \
        char * const* new_argv = generate_fake_argv(python_script, path, argv); \
        char const *fake_path = new_argv[0];                        \
        status = func(fake_path, new_argv, envp, 1);                 \
        free(python_script);\
        free_fake_argv(new_argv);\
    }                                                       \
    else{                                                   \
        free(python_script);                                 \
        status = func(path, argv, envp, 0);                    \
    }    

#define EXECVE_MACRO(func, path, argv, envp) \
    EXEC_MACRO(func, path, argv, envp);  

#define EXECL_MACRO(func, path, argv, envp, FREEARGV) \
    EXEC_MACRO(func, path, argv, envp);                 \
    FREEARGV;                                           

#define EXECV_MACRO(func, path, argv, envp) \
    EXEC_MACRO(func, path, argv, envp);


#define EXECVPE_MACRO(func, path, argv, envp) \
    EXEC_MACRO(func, path, argv, envp);

#define EXECVP_MACRO(func, path, argv, envp) \
    EXEC_MACRO(func, path, argv, envp);

#define EXECLE_MACRO(func, path, argv, envp, FREEARGV)  \
    EXEC_MACRO(func, path, argv, envp);                 \
    FREEARGV;

#define EXECLP_MACRO(func, path, argv, envp, FREEARGV)  \
    EXEC_MACRO(func, path, argv, envp);                                         \
    FREEARGV;

#define POSIX_SPAWN_MACRO(func, pid, file, file_actions, attrp, argv, envp) \
    char *python_script = python_handler_assign(basename(argv[0]));   \
    int status = 0;                                             \
    if(strcmp(python_script, "IGNORE") != 0){               \
        char * const* new_argv = generate_fake_argv(python_script, path, argv); \
        char const *fake_file = new_argv[0];                        \
        status = func(pid, fake_file, file_actions, attrp, new_argv, envp, 1);\
        free(python_script);\
        free_fake_argv(new_argv);\
    }                                                       \
    else{                                                   \
        free(python_script);                                 \
        status = func(pid, file, file_actions, attrp, argv, envp, 0);\
    }

#define POSIX_SPAWNP_MACRO(func, pid, file, file_actions, attrp, argv, envp) \
    POSIX_SPAWN_MACRO(func, pid, file, file_actions, attrp, argv, envp)

#ifdef HAVE_EXECL
int execl(const char *path, const char *arg0, ... /*, (char *)0 */){
    va_list args;
    va_start(args, arg0);
    char * const*argv = convert_to_ptrs_array(arg0, &args);
    va_end(args);
    EXECL_MACRO(execl_wrapper, path, argv, environ, free_ptr_array(argv));
    return status;
}
#endif

#ifdef HAVE_EXECVE
int execve(const char *path, char *const argv[], char *const envp[]){
    EXECVE_MACRO(execve_wrapper, path, argv, envp);
    return status;
}
#endif

#ifdef HAVE_EXECV
int execv(const char *pathname, char *const argv[]){
    EXECV_MACRO(execve_wrapper, pathname, argv, environ);
    return status;
}
#endif

#ifdef HAVE_EXECVPE
int execvpe(const char *file, char *const argv[], char *const envp[]){
    EXECVPE_MACRO(execvpe_wrapper, file, argv, envp);
    return status;
}
#endif

#ifdef HAVE_EXECVP
int execvp(const char *file, char *const argv[]){
    EXECVP_MACRO(execvp_wrapper, file, argv, environ);
    return status;
}
#endif

#ifdef HAVE_EXECLE
int execle(const char *pathname, const char *arg, ... /*, (char *) NULL, char *const envp[] */){
    va_list args;
    va_start(args, arg);
    char *const *argv = convert_to_ptrs_array(arg, &args);
    char *const *envp = va_arg(args, char *const *);
    va_end(args);
    EXECLE_MACRO(execle_wrapper, pathname, argv, envp, free_ptr_array(argv));
    return status;
}
#endif

#ifdef HAVE_EXECLP
int execlp(const char *file, const char *arg, ...){
    va_list args;
    va_start(args, arg);
    char *const *argv = convert_to_ptrs_array(arg, &args);
    EXECLP_MACRO(execlp_wrapper, file, argv, environ, free_ptr_array(argv));
    return status;
}
#endif

#ifdef HAVE_POSIX_SPAWN
int posix_spawn(pid_t *restrict pid, const char *restrict path,
		const posix_spawn_file_actions_t *file_actions,
		const posix_spawnattr_t *restrict attrp,
		char *const argv[restrict], char *const envp[restrict]) {
        POSIX_SPAWN_MACRO(posix_spawn_wrapper, pid, path, file_actions, attrp, argv, envp);
        return status;
}
#endif

#ifdef HAVE_POSIX_SPAWNP
#ifdef HAVE_POSIX_SPAWN
int posix_spawnp(pid_t *restrict pid, const char *restrict path,
		const posix_spawn_file_actions_t *file_actions,
		const posix_spawnattr_t *restrict attrp,
		char *const argv[restrict], char *const envp[restrict]) {
        POSIX_SPAWNP_MACRO(posix_spawnp_wrapper, pid, path, file_actions, attrp, argv, envp);
        return status;
}
#endif
#endif

// 对execve, execvp, execvpe详细定义包装函数是因为对于带p和不带p的exec函数在对path的处理不同，execvpe特殊处理是因为execvpe不使用envp参数
// 不能用于execvp的抽象包装
#ifdef HAVE_EXECVE
int execve_wrapper(const char *path, char *const argv[], char *const envp[], int unset){
    typedef int (*func)(const char*, char *const *, char *const *);
    DLSYM(func, fp, "execve");

    // 需要处理正常执行和捕捉执行后，是否需要对环境变量进行继承传递
    char ** new_env;
    new_env = update_exec_env(envp, &capture_env);
    if(unset){
        unset_exec_env(new_env, unset_env_names, unset_env_length);
    }

    int result = (*fp)(path, argv, (char *const *)new_env);
    free_ptr_array(new_env);
    return result;
}
#endif

#ifdef HAVE_EXECL
#ifndef HAVE_EXECVE
#error no implement execve
#endif
int execl_wrapper(char const *path, char *const *argv, char *const *envp, int unset){
    return execve_wrapper(path, argv, envp, unset);
}
#endif

#ifdef HAVE_EXECV
#ifndef HAVE_EXECVE
#error no implement execve
#endif
int execv_wrapper(char const *path, char *const *argv, char *const*envp, int unset){
    return execve_wrapper(path, argv, envp, unset);
}
#endif

#ifdef HAVE_EXECVPE
int execvpe_wrapper(char const *path, char *const* argv, char *const* envp, int unset){
    typedef int (* func)(char const *, char *const *, char *const*);
    
    DLSYM(func, fp, "execvpe");

    char ** new_env;
    new_env = update_exec_env(envp, &capture_env);
    if (unset){
        unset_exec_env(new_env, unset_env_names, unset_env_length);
    }

    int result = (*fp)(path, argv, new_env);
    free_ptr_array(new_env);

    return result;
}
#endif

#ifdef HAVE_EXECVP
int execvp_wrapper(const char *file, char *const argv[], char *const *envp, int unset){
    typedef int (*func)(const char *, char *const *);
    DLSYM(func, fp, "execvp");

    char **original_env = environ;
    char **new_env = update_exec_env(envp, &capture_env);
    if (unset){
        unset_exec_env(new_env, unset_env_names, unset_env_length);
    }
    environ = new_env;
    int result = (*fp)(file, argv);
    free_ptr_array(new_env);
    environ = original_env;
    return result;
}
#endif

#ifdef HAVE_EXECLE
#ifndef HAVE_EXECVE
#error no implement execve
#endif
int execle_wrapper(const char *file, char * const *argv, char *const *envp, int unset){
    return execve_wrapper(file, argv, envp, unset);
}
#endif

#ifdef HAVE_EXECLP
#ifndef HAVE_EXECVP
#error no implement execvp
#endif
int execlp_wrapper(const char *file, char *const *argv, char *const *envp, int unset){
    return execvp_wrapper(file, argv, envp, unset);
}
#endif


#ifdef HAVE_POSIX_SPAWN
int posix_spawn_wrapper(pid_t *restrict pid, const char *restrict path,
		const posix_spawn_file_actions_t *file_actions,
		const posix_spawnattr_t *restrict attrp,
		char *const argv[restrict], char *const envp[restrict], int unset) {
    typedef int (*func)(pid_t *restrict, const char *restrict, const posix_spawn_file_actions_t *, const posix_spawnattr_t  *restrict, char *const*, char *const*);
    DLSYM(func, fp, "posix_spawn");
    char **new_env = update_exec_env(envp, &capture_env);
    if (unset)
        unset_exec_env(new_env, unset_env_names, unset_env_length);
    int ret = fp(pid, path, file_actions, attrp, argv, (char *const *restrict)new_env);
    free_ptr_array(new_env);
    return ret;
}
#endif

#ifdef HAVE_POSIX_SPAWNP
int posix_spawnp_wrapper(pid_t *restrict pid, const char *restrict path,
		const posix_spawn_file_actions_t *file_actions,
		const posix_spawnattr_t *restrict attrp,
		char *const argv[restrict], char *const envp[restrict], int unset) {
    typedef int (*func)(pid_t *restrict, const char *restrict, const posix_spawn_file_actions_t *, const posix_spawnattr_t      *restrict, char *const*, char *const*);
    DLSYM(func, fp, "posix_spawnp");
    char **new_env = update_exec_env(envp, &capture_env);
    if (unset)
        unset_exec_env(new_env, unset_env_names, unset_env_length);
    int ret = fp(pid, path, file_actions, attrp, argv, (char *const *restrict)new_env);
    free_ptr_array(new_env);
    return ret;
}
#endif


#ifdef HAVE_REMOVE
int remove(const char *path){
    typedef int (*func)(const char *);
    DLSYM(func, fp, __FUNCTION__);
    struct stat statbuf;
    int res = lstat(path, &statbuf);
    if (res != -1 && S_ISREG(statbuf.st_mode) && filter_useless_file(path) != 0){
        // may source file, communicate with python script
        GENERATE_HANDLE_COMMAND(path);
        EXECUTE_CLEAN_COMMAND(cmd);
    }
    int ret = fp(path);
    return ret;
}
#endif

#ifdef HAVE_UNLINK
int unlink(const char *path){
    typedef int (*func)(const char *);
    DLSYM(func, fp, __FUNCTION__);
    struct stat statbuf;
    int res = lstat(path, &statbuf);
    if (res != -1 && S_ISREG(statbuf.st_mode) && filter_useless_file(path) != 0){
        // may source file, communicate with python script
        GENERATE_HANDLE_COMMAND(path);
        EXECUTE_CLEAN_COMMAND(cmd);
    }
    int ret = fp(path);
    return ret;
}
#endif

#ifdef HAVE_UNLINKAT
int unlinkat(int fd, const char *path, int flag){
    typedef int (*func)(int, const char*, int);
    DLSYM(func, fp, __FUNCTION__);
    struct stat statbuf;
    int res = lstat(path, &statbuf);
    char *abs_path = NULL;
    if (is_relative_path(path) == 1){
        char *base_path = get_relative_base_path(fd);
        size_t len = strlen(base_path) + strlen(path) + 2; // "/" + "\0"
        abs_path = (char *)calloc(len, sizeof(char));
        snprintf(abs_path, len, "%s/%s", base_path, path);
        free(base_path);
    }else{
        abs_path = strdup(path);
    }
    if (!filter_useless_file(abs_path)){
        GENERATE_HANDLE_COMMAND(abs_path);
        EXECUTE_CLEAN_COMMAND(cmd);
    }
    free(abs_path);
    int ret = fp(fd, path, flag);
    return ret;
}
#endif


#ifdef HAVE_RENAME
int rename(const char *old, const char *new){
    typedef int (*func)(const char *, const char *);
    DLSYM(func, fp, __FUNCTION__);
    if (filter_useless_file(old) != 0){
        GENERATE_HANDLE_COMMAND(old);
        EXECUTE_CLEAN_COMMAND(cmd);
    }
    int ret = fp(old, new);
    return ret;
}
#endif


#ifdef HAVE_RENAMEAT
int renameat(int oldfd, const char *old, int newfd, const char *new){
    typedef int (*func)(int, const char *, int, const char *);
    DLSYM(func, fp, __FUNCTION__);
    char *base_path = NULL;
    char *old_abspath = NULL;
    char *new_abspath = NULL;
    if (is_relative_path(old) == 1){
        base_path = get_relative_base_path(oldfd);
        size_t len = strlen(base_path) + strlen(old) + 2;
        old_abspath = (char *)calloc(len, sizeof(char));
        snprintf(old_abspath, len, "%s/%s", base_path, old);
        free(base_path);
        base_path = NULL;
    }else{
        old_abspath = strdup(old);
    }
    
    if (!filter_useless_file(old_abspath)){
        GENERATE_HANDLE_COMMAND(old_abspath);
        EXECUTE_CLEAN_COMMAND(cmd);
    }
    free(old_abspath);
    // free(new_abspath);
    int ret = fp(oldfd, old, newfd, new);
    return ret;
}
#endif

// debug时使用，输出参数列表
static void print_argv(char *const *argv){
    for(char *const *it = argv; (it) && (*it); ++it){
        printf("%s ", *it);
    }
    printf("\n");
}

// free字符串二级指针数组
static void free_ptr_array(char * const*argv){
    for (char * const*it = argv; (it) && (*it); ++ it){
        free((void *)(*it));
    }

    free((void **)argv);
}

// 将可变参数转换成参数数组
static char * const* convert_to_ptrs_array(const char *item, va_list *items){
    // 为了execle取参数envp， 需要传va_list的地址进来
    char **array = 0;
    size_t size = 1;

    for(const char *it = item; it && (*it); it = va_arg(*items, const char *)){
        array = (char **)realloc(array, size * sizeof(char const *));
        if(!array){
            ERROR_PRINT("realloc failed");
        }

        char *copy = strdup(it);

        if(!copy){
            ERROR_PRINT("strdup failed")
        }

        array[size - 1] = copy;
        ++ size;
    }

    array = (char **)realloc(array, size * sizeof(char const *));
    if(!array){
        ERROR_PRINT("realloc failed");
    }
    array[size - 1] = 0;

    return array;
}

// 更新环境变量
static char ** update_exec_env(char * const envp[], env_t *cap_env){
    // 1. 复制需要传递的环境变量
    // 2. 将capture_env更新到复制环境变量中
    char ** env_copy = string_array_copy(envp);
    // for(char const **it = env_copy; (it) && (*it); ++it){
    //     printf("%s\n", it);
    // }
    for(int i=0; i<ENV_SIZE && (*cap_env)[i]; ++i){
        update_exec_enviorn(env_copy, capture_env_names[i], (*cap_env)[i]);
    }
    return env_copy;
}

// 对单个环境变量进行修改
static void update_exec_enviorn(char *envp[], char const *env_key, char const *env_value){
    
    // TODO: 问题存疑点
    char ** env_copy = envp;

    size_t key_length = strlen(env_key);

    char ** it = env_copy;

    for(; (it) && (*it); ++it){
        
        // 1. 看key是否已经存在在环境变量中
        if(strncmp(env_key, *it, key_length) == 0 && strlen(*it) > key_length && (*it)[key_length] == '='){
            break;
        }
    } 

    size_t value_length = strlen(env_value);
    size_t env_length = key_length + value_length + 2; // '=' & '\0'
    size_t origin_env_length = 0;

    if ((it) && (*it)){
        if (strstr(*it, env_value) != NULL){ // 已经存在该键值 TODO: 不能够覆盖所有的情况
            return;
        }
        else{
            origin_env_length = strlen(*it);
            env_length = origin_env_length + value_length + 2; // ':' & '\0'
        }
    }

    char * new_env = (char *)malloc(env_length * sizeof(char));
    if (!new_env){
        ERROR_PRINT("malloc failed");
    }

    // 创建新的环境变量
    if(snprintf(new_env, env_length, "%s=%s", env_key, env_value) < 0){
        ERROR_PRINT("snprintf failed");
    }

    if(origin_env_length != 0){
        if( snprintf(new_env + key_length + value_length + 1, \
            env_length - key_length - value_length - 1, ":%s", \
            (*it) + key_length + 1) < 0){  
            ERROR_PRINT("snprintf failed");                 
        }
    }
    
    free(*it);
    *it = strdup(new_env);
    free(new_env);
}

// 重置部分环境变量
static void unset_exec_env(char **env, const char **unset_env_names, const int unset_env_length){
    char **it = env;
    
    for(int i=0; i<unset_env_length; ++i){
        for(it = env; (it) && (*it); ++it){
            if(0 == strncmp(unset_env_names[i], *it, strlen(unset_env_names[i])) && (*it)[strlen(unset_env_names[i])] == '='){
                break;
            }
        }

        if(it && (*it)){
            for(char **ii=it; (ii) && (*ii); ++ii){
                *ii = *(ii + 1);
            }
        }
    }
}

// 复制指针数组
static char ** string_array_copy(char * const in[]){
    
    size_t size = size_of_array(in);
    char **out = (char **)malloc((size + 1) * sizeof(char *));
    if (!out){
        ERROR_PRINT("malloc failed");
    }
    char **out_ptr = out;

    for(char * const*it = in; (it) && (*it); ++ it, ++out_ptr){
        *out_ptr = strdup(*it);
        if (!(*out_ptr)){
            ERROR_PRINT("strdup failed");
        }
    }

    out[size] = 0;

    return out;
}

// 释放二维指针
static void free_fake_argv(char *const* argv){
    for (char * const* it = argv; (it) && (*it); ++ it){
        free((void *)(*it));
    }

    free((void **)argv);
}

// 获取以NULL结尾的指针数组的长度
static int size_of_array(char * const *array){
    // array 以NULL结尾
    int cnt = 0;
    while(array[cnt] != NULL) ++ cnt;
    return cnt;
}

// 发送给python脚本处理，生成新的调用参数列表
static char ** generate_fake_argv(char *python_script, char const* path, char *const*argv){
    char *sep = ":";
    char *python_script_str = python_script;
    if(!python_script_str){
        ERROR_PRINT("strdup failed");
    }
    char *python_path = strsep(&python_script_str, sep);

    size_t raw_argv_size = size_of_array(argv);
    size_t new_argv_size = raw_argv_size + 4; // python script path argv[] NULL

    char ** new_argv = (char **)malloc(new_argv_size * sizeof(char *));
    if (!new_argv){
        ERROR_PRINT("malloc failed");
    }
    char ** argv_ptr = new_argv;

    *(argv_ptr ++) = strdup(python_path);
    size_t real_sript_path_len = strlen(python_script_str) + 2; // '\' + '\0'
    char *real_script_path = (char *)malloc(real_sript_path_len * sizeof(char));
    if(0 > snprintf(real_script_path, real_sript_path_len, "%s",  python_script_str)){
        ERROR_PRINT("snprintf failed");
    }
    *(argv_ptr ++) = real_script_path;
    *(argv_ptr ++) = strdup(path);
    while(raw_argv_size > 0){
        *(argv_ptr ++) = strdup(*(argv ++));
        -- raw_argv_size;
    }

    *(argv_ptr) = NULL;
    return new_argv;
}

// 从环境变量value中提取出编译器配置，python路径，python脚本路径，格式为编译器1?编译器2;python路径:python脚本路径
static int extract_comps_and_wrapper(const char *comp_wrapper_string, char ***ptr_comps, char **ptr_path_to_wrapper){

    char *sep_one = "?";
    char *sep_two = ";";
    char *sep_three = ":";

    char *comps_wrapper_str = strdup(comp_wrapper_string);
    if (!comps_wrapper_str){
        ERROR_PRINT("strdup failed");
    }

    char *path_to_wrapper = comps_wrapper_str;
    char *comps_str = strsep(&path_to_wrapper, sep_two);

    int count = 1;
    const char *tmp = comps_str;
    while(tmp = strstr(tmp, sep_one)){
       count++;
       tmp++;
    }

    char **comps = (char **)malloc(count * sizeof(char *));
    if (!comps){
        ERROR_PRINT("malloc failed");
    }

    int i = 0;
    for (; i < count; i++) {
        char *left_string = strsep(&comps_str, sep_one);
        comps[i] = strdup(left_string);
    }

    *ptr_comps = comps;
    *ptr_path_to_wrapper = strdup(path_to_wrapper);

    free(comps_wrapper_str);

    return count;
}

// 查询字符串是否在字符数组中
static int str_in_strs(const char *str, const char **strs, size_t num){
    for(int it=0; it < num; ++it){
        if(strcmp(str, strs[it]) == 0){
            return 1;
        }
    }
    return 0;
}

// 根据编译器来分配python处理脚本
static char *python_handler_assign(char const *compiler){
    // 先做初步的筛选

    int compilers_count = 0;

    char *python_script;
    char **compilers_set;
    char *return_str;

    for (int i=0; IGNORE_LIST[i] != NULL; i++){
        if (strcmp(IGNORE_LIST[i], compiler) == 0){
            return strdup("IGNORE");
        }
    }

    // GCC
    compilers_count = extract_comps_and_wrapper(capture_env[IRGEN_GCC_ENV_I], &compilers_set, &python_script);
    return_str = SEARCH_COMPS(compiler, (const char **)compilers_set, python_script, compilers_count);
    RETURN_STR(return_str, python_script, compilers_set);

    // CLang
    compilers_count = extract_comps_and_wrapper(capture_env[IRGEN_CLANG_ENV_I], &compilers_set, &python_script);
    return_str = SEARCH_COMPS(compiler, (const char **)compilers_set, python_script, compilers_count);
    RETURN_STR(return_str, python_script, compilers_set);

    // LD
    compilers_count = extract_comps_and_wrapper(capture_env[IRGEN_LINK_ENV_I], &compilers_set, &python_script);
    return_str = SEARCH_COMPS(compiler, (const char **)compilers_set, python_script, compilers_count);
    RETURN_STR(return_str, python_script, compilers_set);

    // AR
    compilers_count = extract_comps_and_wrapper(capture_env[IRGEN_ARCHIVE_ENV_I], &compilers_set, &python_script);
    return_str = SEARCH_COMPS(compiler, (const char **)compilers_set, python_script, compilers_count);
    RETURN_STR(return_str, python_script, compilers_set);

    // Strip
    compilers_count = extract_comps_and_wrapper(capture_env[IRGEN_STRIP_ENV_I], &compilers_set, &python_script);
    return_str = SEARCH_COMPS(compiler, (const char **)compilers_set, python_script, compilers_count);
    RETURN_STR(return_str, python_script, compilers_set);

    // Ranlib
    compilers_count = extract_comps_and_wrapper(capture_env[IRGEN_RANLIB_ENV_I], &compilers_set, &python_script);
    return_str = SEARCH_COMPS(compiler, (const char **)compilers_set, python_script, compilers_count);
    RETURN_STR(return_str, python_script, compilers_set);

    return strdup("IGNORE");
}

static int is_relative_path(char const *path){
    if (path[0] == '/')
        return 0;
    return 1;
}

static char *get_filepath_by_fd(int fd){
    char *sylink = (char *)calloc(PATH_MAX+1, sizeof(char));
    if (snprintf(sylink, PATH_MAX+1, "%s/%d", FIELDESCRIPTORDIR, fd) < 0){
        perror("[ERROR] snprintf error");
        exit(EXIT_FAILURE);
    }
    char *filepath = (char *)calloc(PATH_MAX+1, sizeof(char));
    if (readlink(sylink, filepath, PATH_MAX) == -1){
        perror("[ERROR] readlink error!");
        exit(EXIT_FAILURE);
    }
    free(sylink);
    return filepath;
}

static char *get_relative_base_path(int fd){
    char *base_path = (char *)NULL;
    if (fd == AT_FDCWD){
        base_path = getcwd(NULL, PATH_MAX);
    }else{
        char *fd_file = get_filepath_by_fd(fd);
        base_path = strdup(dirname(fd_file));
        free(fd_file);
    }
    return base_path;
}

static int filter_useless_file(char const* file){
    char *file_copy = strdup(file);
    char *basefile = basename(file_copy);
    char *const *ptr = SAVE_SOURCE_LIST;
    for(; ptr&&(*ptr); ++ptr){
        if (is_contains(basefile, *ptr) == 1){
            free(file_copy);
            return 1;
        }
    }
    free(file_copy);
    return 0;
}

static int is_contains(char const* file, char const * pattern){
    char *base = strstr(file, pattern);
    if (base == NULL){
        return 0;
    }
    if (strlen(base) == strlen(pattern)){
        return 1;
    }
    return 0;
}

// 加载捕捉环境配置
static int load_capture_env(env_t *env){
    // 载入命令捕捉的capture环境
    int status = 1;
    for(size_t i=0; i<ENV_SIZE; ++i){
        char const * const env_value = getenv(capture_env_names[i]);
        char const * const env_copy = env_value ? strdup(env_value):env_value;
        (*env)[i] = env_copy;
        status &= (env_copy ? 1:0);
    }
    return status;
}

// 释放捕捉的环境变量配置
static void release_capture_env(env_t *env){
    for(size_t i=0; i<ENV_SIZE; i++){
        free((void *)((*env)[i]));
        (*env)[i] = 0;
    }
}

// 动态库的加载配置
static void load_env(void){
    // 用来载入关注的环境变量
    if(!capture_env_init){
        capture_env_init = load_capture_env(&capture_env);
    }
}

// 动态库的卸载配置
static void unload_env(void){
    // 释放环境变量
    release_capture_env(&capture_env);
    capture_env_init = 0;
}

static char *array_to_string(char *const*array){
    char *const*array_ptr = array;
    ssize_t array_size = 0;
    while(*array_ptr){
        array_size += strlen(*array_ptr);
        array_ptr ++;
    }

    char *new_str = (char *)malloc(array_size + 1);
    new_str[0] = '\0';
    array_ptr = array;

    while(*array_ptr){
        strcat(new_str, *array_ptr);
        array_ptr ++;
    }
    return new_str;
}